{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Overnight returns (Solution)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[Overnight Returns and Firm-Specific Investor Sentiment](https://papers.ssrn.com/sol3/papers.cfm?abstract_id=2554010)\n",
    "\n",
    "> **Abtract**: We explore the possibility that overnight returns can serve as a measure of firm-specific investor sentiment by analyzing whether they exhibit characteristics expected of a sentiment measure. First, we document short-term persistence in overnight returns, consistent with existing evidence of short-term persistence in share demand of sentiment-influenced retail investors. Second, we find that short-term persistence is stronger for harder-to-value firms, consistent with evidence that sentiment plays a larger role when there is less objective data available for valuation. Third, we show that stocks with high (low) overnight returns underperform (outperform) over the longer-term, consistent with evidence of temporary sentiment-driven mispricing.  \n",
    "\n",
    "> **p 2, I**: The recent work of Berkman, Koch, Tuttle, and Zhang (2012) suggests that a stock’s\n",
    "overnight (close-to-open) return can serve as a measure of firm-level sentiment.\n",
    "\n",
    "> **p 3, I**: Specifically, Berkman et al. (2012) find that attention-generating events (high absolute returns or\n",
    "strong net buying by retail investors) on one day lead to higher demand by individual investors,\n",
    "concentrated near the open of the next trading day...This creates temporary price pressure at the\n",
    "open, resulting in elevated overnight returns that are reversed during the trading day.\n",
    "\n",
    "> **p 3, I**: We conduct three sets of analyses. **In the first\n",
    "we test for short-run persistence in overnight returns.** The basis for expecting this from a\n",
    "measure of sentiment is the evidence in Barber et al. (2009) that the order imbalances of retail\n",
    "investors, who are the investors most likely to exhibit sentiment, persist for periods extending\n",
    "over several weeks...In the third analysis we\n",
    "examine whether stocks with high overnight returns underperform those with low overnight\n",
    "returns over the long term."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Install packages"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import sys"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!{sys.executable} -m pip install -r requirements.txt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import cvxpy as cvx\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "import time\n",
    "import os\n",
    "import quiz_helper\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib inline\n",
    "plt.style.use('ggplot')\n",
    "plt.rcParams['figure.figsize'] = (14, 8)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### data bundle"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import quiz_helper\n",
    "from zipline.data import bundles"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "os.environ['ZIPLINE_ROOT'] = os.path.join(os.getcwd(), '..', '..','data','module_4_quizzes_eod')\n",
    "ingest_func = bundles.csvdir.csvdir_equities(['daily'], quiz_helper.EOD_BUNDLE_NAME)\n",
    "bundles.register(quiz_helper.EOD_BUNDLE_NAME, ingest_func)\n",
    "print('Data Registered')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Build pipeline engine"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from zipline.pipeline import Pipeline\n",
    "from zipline.pipeline.factors import AverageDollarVolume\n",
    "from zipline.utils.calendars import get_calendar\n",
    "\n",
    "universe = AverageDollarVolume(window_length=120).top(500) \n",
    "trading_calendar = get_calendar('NYSE') \n",
    "bundle_data = bundles.load(quiz_helper.EOD_BUNDLE_NAME)\n",
    "engine = quiz_helper.build_pipeline_engine(bundle_data, trading_calendar)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### View Data¶\n",
    "With the pipeline engine built, let's get the stocks at the end of the period in the universe we're using. We'll use these tickers to generate the returns data for the our risk model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "universe_end_date = pd.Timestamp('2016-01-05', tz='UTC')\n",
    "\n",
    "universe_tickers = engine\\\n",
    "    .run_pipeline(\n",
    "        Pipeline(screen=universe),\n",
    "        universe_end_date,\n",
    "        universe_end_date)\\\n",
    "    .index.get_level_values(1)\\\n",
    "    .values.tolist()\n",
    "    \n",
    "universe_tickers"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Get Returns data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from zipline.data.data_portal import DataPortal\n",
    "\n",
    "data_portal = DataPortal(\n",
    "    bundle_data.asset_finder,\n",
    "    trading_calendar=trading_calendar,\n",
    "    first_trading_day=bundle_data.equity_daily_bar_reader.first_trading_day,\n",
    "    equity_minute_reader=None,\n",
    "    equity_daily_reader=bundle_data.equity_daily_bar_reader,\n",
    "    adjustment_reader=bundle_data.adjustment_reader)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Get pricing data helper function"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from quiz_helper import get_pricing"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## get pricing data into a dataframe"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "returns_df = \\\n",
    "    get_pricing(\n",
    "        data_portal,\n",
    "        trading_calendar,\n",
    "        universe_tickers,\n",
    "        universe_end_date - pd.DateOffset(years=5),\n",
    "        universe_end_date)\\\n",
    "    .pct_change()[1:].fillna(0) #convert prices into returns\n",
    "\n",
    "returns_df"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Sector data helper function\n",
    "We'll create an object for you, which defines a sector for each stock.  The sectors are represented by integers.  We inherit from the Classifier class.  [Documentation for Classifier](https://www.quantopian.com/posts/pipeline-classifiers-are-here), and the [source code for Classifier](https://github.com/quantopian/zipline/blob/master/zipline/pipeline/classifiers/classifier.py)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from zipline.pipeline.classifiers import Classifier\n",
    "from zipline.utils.numpy_utils import int64_dtype\n",
    "class Sector(Classifier):\n",
    "    dtype = int64_dtype\n",
    "    window_length = 0\n",
    "    inputs = ()\n",
    "    missing_value = -1\n",
    "\n",
    "    def __init__(self):\n",
    "        self.data = np.load('../../data/project_4_sector/data.npy')\n",
    "\n",
    "    def _compute(self, arrays, dates, assets, mask):\n",
    "        return np.where(\n",
    "            mask,\n",
    "            self.data[assets],\n",
    "            self.missing_value,\n",
    "        )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sector = Sector()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## We'll use 2 years of data to calculate the factor"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Note:** Going back 2 years falls on a day when the market is closed. Pipeline package doesn't handle start or end dates that don't fall on days when the market is open. To fix this, we went back 2 extra days to fall on the next day when the market is open."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "factor_start_date = universe_end_date - pd.DateOffset(years=2, days=2)\n",
    "factor_start_date"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Walk through \"Returns\" class\n",
    "\n",
    "We'll walk through how the `Returns` class works, because we'll create a new class that inherits from `Returns` in order to calculate a customized return.\n",
    "\n",
    "### Returns inherits from CustomFactor\n",
    "The zipline package has a class [zipline.pipeline.factors.Returns](https://www.zipline.io/appendix.html?highlight=returns#zipline.pipeline.factors.Returns) which inherits from class [zipline.pipeline.CustomFactor](https://www.zipline.io/appendix.html?highlight=custom%20factor#zipline.pipeline.CustomFactor).  The [source code for Returns is here](https://www.zipline.io/_modules/zipline/pipeline/factors/basic.html#Returns), and the [source code for CustomFactor is here.](https://www.zipline.io/_modules/zipline/pipeline/factors/factor.html#CustomFactor) \n",
    "\n",
    "**Please open the links to the documentation and source code and follow along with our notes about the code**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Inputs variable\n",
    "The CustomFactor class takes the `inputs` as a parameter of the constructor for the class, otherwise it looks for a class-level variable named `inputs`.  `inputs` takes a list of BoundColumn instances.  These help us choose what kind of price-volume data to use as input.  The `Returns` class sets this to\n",
    "```\n",
    "inputs = [USEquityPricing.close]\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### USEquityPricing class\n",
    "The class [USEquityPricing](https://www.zipline.io/appendix.html?highlight=usequitypricing#zipline.pipeline.data.USEquityPricing) has a couple BoundColumn instances that we can choose from.\n",
    "close = USEquityPricing.close  \n",
    "high = USEquityPricing.high  \n",
    "low = USEquityPricing.low  \n",
    "open = USEquityPricing.open  \n",
    "volume = USEquityPricing.volume  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Quiz 1\n",
    "If we wish to calculate close to open returns, which columns from USEquityPricing do you think we'll want to put into the list and set as `inputs`?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Quiz 1 Answer\n",
    "`USEquityPricing.open` and `USEquityPricing.close`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### window_length variable\n",
    "The CustomFactor class takes `window_length` (an integer) as a constructor parameter, otherwise it looks for a class-level variable named `window_length`.  If we chose a `window_length = 2` then this means that we'll be passing two days' worth of data (two rows) into the `compute` function."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Quiz 2\n",
    "What window length would you choose if you were calculating daily close to open returns?  Assume we have daily data."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Answer 2\n",
    "window length of 2 to have 2 days of data."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Compute function\n",
    "The function definition of the `Returns` class includes the `compute` function\n",
    "```\n",
    "def compute(self, today, assets, out, close):\n",
    "        out[:] = (close[-1] - close[0]) / close[0]\n",
    "        \n",
    "```\n",
    "* `today`: this is handled by parent classes; it has the datetime for the \"today\" row for the given subset of data.  We won't use it for this function implementation.\n",
    "* `assets`: this is handled by parent classes: it has the column header names for the \"out\" and \"close\".  We won't use it for this function implementation.\n",
    "* `out`: this points to a numpy array that will store the result of our compute.  It stores our \"return\" value of the `compute` function instead of explicitly returning a variable.\n",
    "* `*input`: a tuple of numpy arrays that contain input data that we'll use to compute a signal.  In the `Returns` definition of `compute`, the input is a single value `close`, but we can list more if we need additional columns of data to compute a return.\n",
    "\n",
    "\n",
    "If we set the `window_length=2`, then the `compute` function gets two rows worth of data from `close`.  The index 1 value is the most recent value, and the index 0 value is the earliest in time.  Recall that in Python, the -1 index is the same as getting the highest indexed value, so with a numpy array of just length two, -1 gives us the value at index 1.\n",
    "\n",
    "So the line of code is calculating the one-day return using the close price, and storing that into the `out` variable.\n",
    "\n",
    "$ Return = \\frac{close_1 - close_0}{close_0} $ "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Quiz 3\n",
    "Given a numpy array for open prices called `open` and a numpy array for close prices called `close`, what code would you write to get the most recent open price?  Assume that you have 2 days of data."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Answer 3\n",
    "open[1] or open[-1] (because python lets us do reverse indexing) are valid answers.  Use the -1 index allows us to get the most recent price (the very last index of the numpy array) regardless of the window length, so you may prefer to use -1 to make your code easier to maintain or modify."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Close To Open Returns (Overnight Returns)\n",
    "\n",
    "The close-to-open return is the change in price between when the market closed on one day and when it opened on the next.  So it's\n",
    "\n",
    "$ CloseToOpen = \\frac{open_1 - close_0}{close_0}$ \n",
    "\n",
    "We'll now create a class `CTO` that inherits from `Return`, and override the `compute` function."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Quiz 4\n",
    "Create a customized class `CloseToOpenReturns` that inherit from the Returns class.  Define the compute function to calculate overnight returns."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from zipline.pipeline.data import USEquityPricing\n",
    "from zipline.pipeline.factors import Returns"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class CloseToOpenReturns(Returns):\n",
    "    \"\"\"\n",
    "\n",
    "    \"\"\"\n",
    "    \n",
    "    # TODO: Set window_length (we're calculating daily returns)\n",
    "    window_length = 2\n",
    "\n",
    "    # TODO: set inputs\n",
    "    inputs = [USEquityPricing.open, USEquityPricing.close]\n",
    "    \n",
    "    # The compute method is passed the current day, the assets list, a pre-allocated out vector, and the\n",
    "    # factor's items in the list `inputs`\n",
    "    def compute(self, today, assets, out, opens, closes):\n",
    "        #TODO: calculate close-to-open return and save into out[:]\n",
    "        out[:] = (opens[-1] - closes[0]) / closes[0]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Trailing overnight returns\n",
    "\n",
    "The cumulative overnight returns over a week may be predictive of future returns; hence it's a kind of momentum signal.\n",
    "\n",
    "$ TrailingOvernightReturns = \\sum_{1}^{Days}CloseToOpen_t$  \n",
    "Where $Days$ could be 5 if we are looking at a weekly window.\n",
    "\n",
    "So we want to take the `CloseToOpenReturns` as our input into another class, `TrailingOvernightReturns`, which also inherits from `Returns`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### mask\n",
    "Note that we're going to create another class that inherits from `Returns`.  Recall that `Returns` inherits from [CustomFactor](https://www.zipline.io/appendix.html?highlight=factor#zipline.pipeline.CustomFactor), which has a `mask` parameter for its constructor.  The `mask` parameter takes in a `Filter` object, which determines which stock series get passed to the `compute` function.  Note that when we used `AverageDollarVolume` and stored its output in the variable `universe`, this `universe` variable is of type `Filter`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Quiz 5\n",
    "If you wanted to create an object of type CloseToOpen, and also define the object so that it only computes returns on the set of stocks in universe that we selected earlier in this notebook, what code would you write?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Answer 5\n",
    "We could instantiate a CloseToOpen object with `CloseToOpenReturns(mask=universe)`, and this would only calculate close to open returns for the stocks defined in our `universe` variable."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## numpy.nansum\n",
    "Numpy has a `nansum` function that treat NaN (not a number) as zeros.  Note that by default, if we give numpy.nansum a 2D numpy array, it will calculate a single sum across all rows and columns.  For our purposes, we want to compute a sum over 5 days (5 rows), and each column has daily close to open returns for a single stock.  It helps to think of the a matrix (2D numpy array) as a nested list of lists. This makes it easier to decide whether to set `axis=0` or `axis=1`.\n",
    "```\n",
    "tmp = \n",
    "[ \n",
    "  [stock1day1, stock2day1 ]\n",
    "  [stock1day2, stock2day2 ]\n",
    "  ...\n",
    "]\n",
    "```\n",
    "If we look at the outermost list, each element is a list that represents one day's worth of data.  If we used `np.nansum(tmp,axis=0)`, this would sum across the days for each stock.  If we think of this as a 2D matrix, setting `axis=0` is like calculating a sum for each column.\n",
    "\n",
    "If we set `axis=0`, this applies `nansum` to the outermost list (axis 0), so that we end up with:\n",
    "```\n",
    "[\n",
    "  sum_of_stock_1, sum_of_stock_2\n",
    "]\n",
    "```\n",
    "Alternatively, if we set `axis=1`, this applies `nansum` to the lists nested inside the outermost list.  Each of these nested lists represent data for a single day, for all stocks, so that we get:\n",
    "```\n",
    "[\n",
    "  sum_of_day_1,\n",
    "  sum_of_day_2,\n",
    "]\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Example using numpy.nansum"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tmp = np.array([\n",
    "                 [1, 2, 3],\n",
    "                 [np.nan, np.nan, np.nan],\n",
    "                 [1, 1, 1]\n",
    "               ])\n",
    "\n",
    "print(f\"Sum across rows and columns: numpy.nansum(tmp) \\n{np.nansum(tmp)}\")\n",
    "print(f\"Sum for each column: numpy.nansum(tmp,axis=0) \\n{np.nansum(tmp,axis=0)}\")\n",
    "print(f\"Sum for each row: numpy.nansum(tmp,axis=1) \\n{np.nansum(tmp,axis=1)}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Quiz 6\n",
    "For our purposes, we want want a sum for each stock series.  Which axis do you think we should choose?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Answer 6\n",
    "We want to set axis = 0 so that we have a sum for each stock (each column)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Quiz 7\n",
    "Create a class TrailingOvernightReturns that inherits from Returns and takes the cumulative weekly sum of overnight returns."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class TrailingOvernightReturns(Returns):\n",
    "    \"\"\"\n",
    "    Sum of trailing close-to-open returns; we expect sentiment persistence at short horizons, so we\n",
    "    look at the 5-day (ie., 1 week) window\n",
    "    \"\"\"\n",
    "    # TODO: choose a window_length to calculate a weekly return\n",
    "    window_length = 5\n",
    "    \n",
    "    # TODO: set inputs to a list containing the daily close to open returns\n",
    "    # Filter the close to open returns by our stock universe\n",
    "    inputs = [CloseToOpenReturns(mask=universe)]\n",
    "    \n",
    "    def compute(self, today, assets, out, close_to_open):\n",
    "        #TODO: calculate the sum of close_to_open\n",
    "        #choose the axis so that there is a sum for each stock (each column)\n",
    "        #treat NaN as zeros\n",
    "        out[:] = np.nansum(close_to_open, axis=0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Quiz 8\n",
    "Create a factor by instantiating the TrailingOvernightReturns class that you just defined.  Demean by sector, rank and covnert to a zscore."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# TODO: create an overnight_returns_factor variable\n",
    "overnight_returns_factor = (\n",
    "    TrailingOvernightReturns().\n",
    "    demean(groupby=Sector()).\n",
    "    rank().\n",
    "    zscore()\n",
    ")\n",
    "# create a pipeline called p\n",
    "p = Pipeline(screen=universe)\n",
    "p.add(overnight_returns_factor, 'Overnight_Sentiment')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Visualize pipeline"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "p.show_graph(format='png')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## run pipeline and view the factor data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df = engine.run_pipeline(p, factor_start_date, universe_end_date)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Visualize factor returns\n",
    "\n",
    "These are returns that a theoretical portfolio would have if its stock weights were determined by a single alpha factor's values."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from quiz_helper import make_factor_plot"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "make_factor_plot(df, data_portal, trading_calendar, factor_start_date, universe_end_date);"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
